package org.apache.ibatis.builder.annotation;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.binding.MapperProxyFactory;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;

import com.mapperdb.core.Const;
import com.mapperdb.joor.Reflect;
import com.mapperdb.util.MapperUtils;

public class MapperAnntBuilder extends MapperAnnotationBuilder {
	public final static Reflect reflectMappedStatement = Reflect.on(MappedStatement.class);
	protected Reflect reflectBuilder;
	protected Configuration configuration;
	protected Class<?> type;
	protected Map<String, MappedStatement> mappedStatements;
	protected Reflect mapperRegistryReflect;
	protected Map<String, MappedStatement> mappedStatementCache;

	public MapperAnntBuilder(Configuration configuration, Class<?> type) {
		super(configuration, type);
		this.configuration = configuration;
		this.type = type;
	}

	public void afterPropertiesSet() {
		this.reflectBuilder = Reflect.on(configuration);
		this.mappedStatements = reflectBuilder.field(Const.Configuration_mappedStatements).get();
		this.mapperRegistryReflect = reflectBuilder.field(Const.Configuration_mapperRegistry);// 获取 Mapper 注册器
		this.mappedStatementCache = new HashMap<String, MappedStatement>();
	}

	@Override
	public void parse() {
		super.parse();
		try {
			// 移除 Mapper
			Map<Class<?>, MapperProxyFactory<?>> knownMappers = mapperRegistryReflect.field(Const.MapperRegistry_knownMappers)
					.get();
			knownMappers.remove(type);
			// 移除 resource
			Set<String> loadedResources = Reflect.on(configuration).field(Const.Configuration_loadedResources).get();
			loadedResources.remove(type.toString());
			// 重新 buildAllStatements
			configuration.getMappedStatements();
		} catch (Exception e) {
			// ignore
		}
	}

	@Override
	public void parseStatement(Method method) {
		super.parseStatement(method);
		if (!(MapperUtils.getSqlAnnotationType(method) == null || MapperUtils.getSqlProviderAnnotationType(method) == null)) { return; }
		cachedMappedStatement(method);
	}

	/**
	 * @Title: 缓存 MappedStatement
	 * @param method
	 * @return MappedStatement
	 */
	protected MappedStatement cachedMappedStatement(Method method) {
		String mappedStatementId = type.getName() + "." + method.getName();
		MappedStatement mappedStatement = mappedStatementCache.get(mappedStatementId);
		if (mappedStatement == null) {
			mappedStatement = configuration.getMappedStatement(mappedStatementId);
			mappedStatementCache.put(mappedStatementId, mappedStatement);
			mappedStatements.remove(mappedStatementId);// 移除 Configuration 中的 MappedStatement
		}
		return mappedStatement;
	}

	public MappedStatement getMappedStatement(String id) {
		return mappedStatementCache.get(id);
	}

	public boolean hasMappedStatement(String id) {
		return mappedStatementCache.containsKey(id);
	}

	public MappedStatement copyMappedStatement(String id) {
		MappedStatement oldMappedStatement = mappedStatementCache.get(id);
		if (oldMappedStatement == null) return null;

		MappedStatement newMappedStatement = reflectMappedStatement.create().get();

		MetaObject oldMeta = SystemMetaObject.forObject(oldMappedStatement);
		MetaObject newMeta = SystemMetaObject.forObject(newMappedStatement);
		String[] names = oldMeta.getGetterNames();

		Object value = null;
		for (String name : names) {
			value = oldMeta.getValue(name);
			if (value != null) newMeta.setValue(name, oldMeta.getValue(name));
		}
		return newMappedStatement;
	}

	public Map<String, MappedStatement> getMappedStatementCache() {
		return mappedStatementCache;
	}

}
